Problem B: [noip2016十连测第五场]walk (树形dp)

Problem B: [noip2016十连测第五场]walk

Time Limit: 20 Sec   Memory Limit: 233 MB
Submit: 7   Solved: 6
[ Submit][ Status][ Web Board]

Description

完整试题详见:http://www.lydsy.com/JudgeOnline/upload/201610/statements(1).pdf
给定一棵 n 个节点的树,每条边的长度为 1,同时有一个权值w。定义一条路径的权值为路径上所有边的权值的最
大公约数。现在对于任意 i∈[1,n], 求树上所有长度为 i 的简单路径中权值最大的是多少。如果不存在长度为 
i 的路径,则第 i 行输出 0。

Input

第一行,一个整数 n,表示树的大小。
接下来 n-1 行,每行三个整数 u,v,w,表示 u,v 间存在一条权值
为 w 的边。
n≤4*10^5, 1≤u,v≤n, w≤10^6

Output

对于每种长度,输出一行,表示答案。

Sample Input

3
1 2 3
1 3 9

Sample Output

9
3
0

HINT

[ Submit][ Status]

题解:树形dp

这道题我们可以考虑枚举最大公因数,然后求最大公因数为i的最长链。这个需要怎么办呢?我们考虑将每条边拆成因数条边,要是一次性加入的话数量会炸飞,所以我们每次将是当前公因数倍数的边加入,然后求树上的最长链。然后用当前的公因数更新长度为最长链的长度的答案。求解后将数组清零,重复利用。

有些长度可能不会出现在最长链中,因为他是构成最长链的一部分,所以如果这个长度的答案<长度大于他的答案的话,就要将长度大于他的答案给他,从大到小枚举ans[i]=max(ans[i],ans[i+1]).

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 800003
#define M 1000006
using namespace std;
int head[M],nxt1[N],x1[N],y2[N],cnt,sz,ans[N];
int point[M],nxt[N],v[N],tot,st[N],top,vis[N],len;
int n,m,mark;
void add(int k,int x,int y)
{
 	cnt++; nxt1[cnt]=head[k]; head[k]=cnt; x1[cnt]=x; y2[cnt]=y;
}
void add1(int x,int y)
{
	tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
	st[++top]=x; st[++top]=y;
}
int dfs(int x,int fa)
{
	vis[x]=sz;
	int l=0,r=0;
	for (int i=point[x];i;i=nxt[i])
	 if (vis[v[i]]!=sz&&v[i]!=fa){
	 	int t=dfs(v[i],x);
	 	if (t+1>l) r=l,l=max(l,t+1);
	 	else if (t+1>r) r=max(r,t+1);
	 }
	len=max(len,l+r);
	return l;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++)
     {
     	int a,b,c; scanf("%d%d%d",&a,&b,&c);
     	add(c,a,b);
	 }
	for (int i=1;i<=1000000;i++){
		for (int j=i;j<=1000000;j+=i)
		 for (int k=head[j];k;k=nxt1[k])
		  add1(x1[k],y2[k]);
		mark=i; ++sz; len=0;
		for (int j=1;j<=top;j++)
		 if (vis[st[j]]!=sz) dfs(st[j],0);
		ans[len]=max(ans[len],i);
		for (int j=1;j<=top;j++)
		 point[st[j]]=0;
		tot=0; top=0;
	}
	for (int i=n;i>=1;i--)  ans[i]=max(ans[i],ans[i+1]);
	for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值